home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power CD-ROM!! 8
/
Power CD-ROM 8.iso
/
prgmming
/
oldutil
/
adjram30.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-12-10
|
36KB
|
1,110 lines
/* Adjustable Ram Disk
(c) Copyright 1986 by Gary Cramblitt. All Rights Reserved.
v2.2 1 Jul 86 Initial version
v2.3 24 Aug 86 Bug. FAT media byte not updated properly.
v2.4 29 Aug 86 If current drive is memory disk drive, reset current
directory on exit.
v2.5 30 Aug 86 Increase FAT to permit max size of 2043K;
Increase size of root directory to 128 entries;
Start code for /E option (Expanded Memory Support)
v3.0 30 Aug 86 Finish code for /E option.
For program usage, see the last routine.
This program is coded in DeSmet C v2.4. Any function beginning
with underscore (_) is a non-standard routine from the DeSmet library.
They are:
_showcs()
Synopsis: unsigned _showcs()
Returns the current value of CS.
_showds()
Synopsis: unsigned _showds()
Returns the current value of DS (= SS in DeSmet C).
_peek()
Synopsis: char _peek(offset,segment)
char *offset;
unsigned segment;
Returns the byte at specified far address.
_doint()
Synopsis: set any or all of the externs
_rax,_rbx,_rcx,_rdx,_rsi,_rdi,_res,_rds
followed by
_doint(interrupt number);
Performs the specified interrupt with the specified
registers set from the externs.
After the call, _rax, etc. can be used. _carryf
and _zerof are extern char variables set to 1 if
the carry or zero flag is set.
In addition, the following routines are semi_standard, and may have
slightly different implementations with your compiler:
strncmp()
Synopsis: char *strncmp(leftstring,rightstring,max)
char *leftstring, *rightstring
int max
strncmp() compares up to a specified number of chars
in two strings. It returns 0 if the specified number
of characters in the string are the same.
free()
Synopsis: int free(pointer)
char *pointer
free memory previously allocated (by calloc). Called
"cfree" in K&R.
*/
/* ==== Definitions ==== */
/* ---- Overall Definitions ---- */
#define true 1
#define false 0
#define max_size 2043 /* max memory disk size */
/*
---- To compile without LOTUS/INTEL/Microsoft Expanded Memory
support, change the "1" to "0" in the next statement. This
will save about 2500 bytes in the .EXE file.
*/
#define em_support 1 /* compile for EM support */
/*
The following symbols must correspond to the same symbols in
file amdisk.asm. If one is changed, so must the other.
This is because the first memory block may not be
deallocated.
*/
#define min_size 64 /* minimum 64K = 128 sectors */
#define sec_per_blk 64 /* increment in 32K blocks */
/*
---- Disk definitions. Note: These constants should be made into
variables or functions if this program's algorithms need to
be generalized to disks of any type, especially high density disks.
Since this program works only in conjunction with amdisk.asm,
is OK to make them constants here.
*/
#define bytes_per_sec 512 /* 512 bytes per sector */
#define par_per_sec (512/16)
/* 32 paragraphs per sector */
#define sec_per_K (1024/512)
/* sectors per 1024 bytes */
#define dir_per_sec (512/sizeof(struct dir_entry))
/* directory entries per sector */
#define em_pag_per_blk 2 /* two 16K EM pages per mem block */
/* ---- Program Segment Prefix ---- */
#define environment_segment 0x2c /* segment address of the environment */
/* ---- DOS interrupts ---- */
#define dosi_dosf 0x21 /* DOS function interrupt */
#define dosi_dsk_read 0x25 /* DOS absolute disk read interrupt */
#define dosi_dsk_write 0x26 /* DOS absolute disk write interrupt */
/* ---- User interrupts ---- */
#define usri_emm 0x67 /* LOTUS/INTEL/Microsoft Expanded
Memory Manager */
/* ---- DOS functions ---- */
#define dosf_seldisk 0x0e /* set default disk */
#define dosf_getdisk 0x19 /* get current default disk */
#define dosf_getver 0x30 /* get DOS version number */
#define dosf_keepprc 0x31 /* keep process (term and stay resident) */
#define dosf_drvfre 0x36 /* get disk free space */
#define dosf_chdir 0x3b /* set default directory */
#define dosf_openh 0x3d /* open file handle */
#define dosf_closeh 0x3e /* close file handle */
#define dosf_ioctl 0x44 /* IOCTL */
#define dosf_cwd 0x47 /* get current directory */
#define dosf_alloc 0x48 /* allocate memory block */
#define dosf_dealloc 0x49 /* deallocate memory block */
#define dosf_setblk 0x4a /* modify memory block */
/* ---- LOTUS/INTEL/Microsoft Expanded Memory Manager functions ---- */
#define emm_status 0x40 /* get manager status */
#define emm_get_PFseg 0x41 /* get page frame segment */
#define emm_get_pages 0x42 /* get number of pages */
#define emm_get_handle 0x43 /* get handle and allocate memory */
#define emm_map_memory 0x44 /* map memory */
#define emm_fre_handle 0x45 /* free handle and memory */
#define emm_get_ver 0x46 /* get EMM version */
#define emm_sav_map 0x47 /* save mapping context */
#define emm_res_map 0x48 /* restore mapping context */
#define emm_num_handles 0x4b /* get number of EMM handles */
#define emm_hdl_pages 0x4c /* get pages owned by handle */
#define emm_all_pages 0x4d /* get pages for all handles */
#define emm_pag_map 0x4e /* get or set page map */
#define nor_flg 0 /* memory block is in normal memory */
#define em_flg 1 /* memory block is in expaned memory */
/*
---- DOS Errors ----
These codes are returned by program and can be tested with DOS
IF statement.
*/
#define dose_noerr 0 /* no error */
#define dose_invfunc 1 /* invalid function */
#define dose_arena 7 /* arena trashed */
#define dose_noram 8 /* not enough memory */
#define dose_invdrv 15 /* invalid drive */
/* ---- Directory definitions ---- */
#define never_used 0 /* directory entry never used */
#define erased 0xe5 /* file has been erased */
#define dir_bit 0x10 /* this bit indicates subdirectory */
/* ---- FAT definitions ---- */
#define available 0 /* cluster is available for use */
#define bad 0xff7 /* cluster is bad */
#define last_low 0xff8 /* last cluster for the file */
#define last_high 0xfff /* last cluster for the file */
extern unsigned _rax, _rbx, _rcx, _rdx, _rsi, _rdi, _res, _rds;
extern char _carryf, _zerof;
/* ---- Directory entry ---- */
struct dir_entry {
union dir_name {
char name[8];
unsigned char status;
} u_name;
char ext[3];
unsigned char attr;
unsigned char reserved[10];
unsigned int time;
unsigned int date;
unsigned int first_cluster;
unsigned long size;
};
/* ==== Global Data Storage ==== */
unsigned int pgm_seg; /* CS at start of program saved here */
int drive_number; /* memory disk drive number A=0, B=1, etc */
int mdisk_size; /* user's desired memory disk size in K */
int mdisk_secs; /* user's desired memory disk size in sectors */
int mdisk_chg; /* the difference between disk's current size
and the desired size (in sectors) */
int free_secs; /* unused sectors in the memory disk */
int first_dir_sector;
int last_dir_sector; /* loc of directory */
int first_fat_sector;
int last_fat_sector; /* loc of FAT */
int free_cluster; /* ptr to first free cluster */
int first_data_sector; /* first sector after the last FAT */
/* ---- Memory Disk Boot Record ---- */
struct boot_record {
unsigned char jmp[3]; /* non-bootable (no jump instruction ) */
char ident[8]; /* identification */
unsigned int bytes_in_sector;/* bytes/sector */
unsigned char sec_per_cluster;/* sectors/cluster */
unsigned int bpb_reserved; /* reserved sectors */
unsigned char bpb_fats; /* number of FAT's */
unsigned int bpb_root; /* directory entries in root */
unsigned int bpb_total; /* total number of sectors */
unsigned char bpb_media; /* media byte = number of mem blocks */
unsigned int bpb_fat_size; /* sectors/FAT */
unsigned int sec_per_track; /* sectors/track */
unsigned int heads; /* number of heads */
unsigned int hidden; /* hidden sectors */
struct mem_blk_table_entry {
unsigned char typ; /* type of blk: 0 = normal 1 = EM */
unsigned int par; /* paragraph address of block */
unsigned int siz; /* number of sectors in the memory block */
unsigned int hdl; /* expanded memory handle */
} mem_blk_table[max_size/sec_per_blk];
char rest_of_record[bytes_per_sec - sizeof(struct boot_record)];
} boot_sec;
/* ---- Memory disk default pathname ----- */
struct pathname {
char drv;
char colon;
char slash;
char dir[64];
} mdisk_pathname; /* Ram disk's current pathname */
/* ---- Default drive ---- */
unsigned char default_drive;
/* ---- Expanded Memory Manager ----- */
unsigned int em_PFseg; /* EMM's page frame segment */
int em_requested; /* true if to expand using EM */
char em_device_name[] = "EMMXXXX0"; /* EMM device name */
/* ---- File Allocation Table ----*/
unsigned char *fat;
/* ---- Declare types of library functions which return non-integer values */
char *calloc(); /* allocates memory for given number of things */
char *strncmp(); /* comapares 2 strings of specified length */
unsigned _showcs(); /* returns current CS register */
char _peek(); /* returns a byte given segment and offset */
/* ==== MAIN ROUTINE ==== */
main(argc, argv)
int argc;
char *argv[];
{
int err_code; /* exit error code ( 0 if no error ) */
int result;
int j;
int k;
int m;
/*
---- Begin by saving the CS, which points to just after the 256-byte
PSP. By subtracting 16, we get the segment pointer for the
program segment. This is, of course, highly DeSmet C dependent
code. Hopefully, your compiler has some mechanism for getting
the program segment pointer.
*/
pgm_seg = _showcs() - 16;
printf("Adjust Memory Disk v3.0 ");
#if em_support
printf("(with EM Support) ");
#endif
printf(" (c) Copyright 1986 by Gary Cramblitt\n");
err_code = dose_noerr; /* set no error */
/* ---- Make sure MS-DOS 2 or above ---- */
_rax = dosf_getver << 8;
_doint(dosi_dosf);
if (_rax & 0x00ff < 2) {
printf ("Error -- this program requires DOS version 2 or above.\n");
exit(dose_invfunc);
}
/*
---- Release our own environment block. It doesn't hurt to do so.
Done so we don't have to do it in the future when user requests
memory disk shrinkage.
*/
_res = peekw(environment_segment, pgm_seg);
_rax = dosf_dealloc << 8;
_doint(dosi_dosf);
if (_carryf == 1) {
printf("Error -- could not free environment block. Error code %d\n",
_rax);
exit(dose_arena);
}
/*
---- Parse the command line drive letter.
If user makes an error, give him usage message and exit.
*/
if (argc < 2 ||
(drive_number = (toupper(*argv[1]) - 65)) < 0 || drive_number > 8) {
dsp_usage();
exit(dose_invfunc);
}
/*
---- Read the boot sector from the memory disk. Verify that we are
dealing with the correct memory disk.
*/
if ((result = readsec(drive_number, 0, &boot_sec)) != 0) {
printf("Error -- could not read boot sector. Error code %1d\n", result);
exit(dose_invdrv);
}
if (strcmp(boot_sec.ident,"AMDISK ") != 0) {
printf("Error -- that drive is not the adjustable memory disk!\n");
exit(dose_invdrv);
}
/* ---- Determine the drive's current size and free space. */
mdisk_size = boot_sec.bpb_total/sec_per_K;
_rax = dosf_drvfre << 8;
_rdx = drive_number + 1;
_doint(dosi_dosf);
if (_rax == 0xffff) {
printf("Error -- Invalid drive letter or memory disk is not loaded.\n");
exit(dose_invdrv);
}
free_secs = _rbx * _rax; /* avail clusters * sectors per cluster */
printf("Drive %c: Total Size: %dK Free Space: %dK\n",
drive_number + 65, mdisk_size, free_secs/sec_per_K);
/* ---- Obtain the default disk. ---- */
_rax = dosf_getdisk << 8;
_doint(dosi_dosf);
default_drive = _rax & 0x00ff;
/* ---- Get the memory disk's current directory. ---- */
mdisk_pathname.drv = drive_number + 'A';
mdisk_pathname.colon = ':';
mdisk_pathname.slash = '\\';
_rax = dosf_cwd << 8;
_rdx = drive_number + 1;
_rds = _showds();
_rsi = mdisk_pathname.dir;
_doint(dosi_dosf);
/* ---- Allocate storage for the FAT and read it in. ---- */
fat = calloc(boot_sec.bpb_fat_size, bytes_per_sec);
first_fat_sector = boot_sec.bpb_reserved;
last_fat_sector = first_fat_sector + boot_sec.bpb_fat_size - 1;
k = 0;
for (j = first_fat_sector; j <= last_fat_sector; j++) {
readsec(drive_number, j, fat + k);
k = k + bytes_per_sec;
}
/*
---- Determine what user wants to do -- expand or shrink the memory
disk, and by how much.
*/
if (argc >= 3) {
if (toupper(*argv[2]) == 'F')
mdisk_chg = atoi(argv[2]+1) - free_secs/sec_per_K;
else if (toupper(*argv[2]) == 'M') {
mdisk_chg = atoi(argv[2]+1) - free_secs/sec_per_K;
if (mdisk_chg < 0) mdisk_chg = 0;
} else {
mdisk_chg = atoi(argv[2]);
if (*argv[2] == '+' || *argv[2] == '-'); else
mdisk_chg = mdisk_chg - (boot_sec.bpb_total/sec_per_K);
}
} else exit(dose_noerr);
mdisk_size = mdisk_chg + (boot_sec.bpb_total/sec_per_K);
mdisk_size = ((mdisk_size + (sec_per_blk/sec_per_K - 1))/
(sec_per_blk/sec_per_K))*(sec_per_blk/sec_per_K); /* round to 32K */
mdisk_secs = mdisk_size * sec_per_K;
mdisk_chg = mdisk_secs - boot_sec.bpb_total;
printf(" Desired Size: %dK Desired Change: %dK\n",
mdisk_size, mdisk_chg/sec_per_K);
if (mdisk_size < min_size || mdisk_size > max_size) {
dsp_usage();
exit(dose_invfunc);
}
/*
---- If expanding into Expanded Memory, check to make sure that
EMM is available. Check is made by opening the EMM device.
If open fails, EMM is not loaded. Get page frame segment from
EMM. Raw DOS handle open is used here to avoid bringing in
huge DeSmet IO routines.
*/
em_requested = false;
#if em_support
if (argc == 4 && toupper(*(argv[3]+1)) == 'E') {
_rax = (dosf_openh << 8) + 0; /* open a handle function */
_rds = _showds();
_rdx = &em_device_name; /* DS:DX => "EMMXXXX0" */
_doint(dosi_dosf);
j = _rax; /* j = returned handle */
if (_carryf == 1) {
printf("Error -- Expanded Memory Manager is not available.\n");
printf(" Error code: %d\n",_rax);
exit(dose_invfunc);
} else {
_rax = (dosf_ioctl << 8) + 7; /* Get output status */
_rbx = j; /* BX = handle */
_doint(dosi_dosf);
_rbx = j; /* BX = handle */
j = _rax & 0xff; /* j = returned device status */
_rax = dosf_closeh << 8; /* close handle */
_doint(dosi_dosf);
if (j == 0xff) {
em_requested = true; /* flag EM requested */
_rax = emm_get_PFseg << 8; /* get EM page frame segment */
_doint(usri_emm);
_rax = _rax >> 8; /* status in AH */
if (_rax == 0) em_PFseg = _rbx;
else {
printf("Error -- Expanded Memory Manager could not report page frame.\n");
printf(" Error code: %2xH\n",_rax);
exit(dose_noram);
}
} else {
printf("Error -- Expanded Memory Manager is not available.\n");
printf(" Error code: %2xH\n",j);
exit(dose_invfunc);
}
}
}
#endif
/*
---- Expand or shrink as indicated. If shrinking, first make
sure there is sufficient room.
*/
if (mdisk_chg < 0 ) {
if (free_secs > -mdisk_chg) err_code = shrink();
else {
printf("Error -- NOT enough room to do the shrink.\n");
printf(" You must first erase some files.\n");
dsp_mem_blk_table();
exit(dose_noerr);
}
} else if (mdisk_chg > 0) err_code = expand();
else printf ("The disk is already that size! Nothing to do.\n");
/* ---- Write the new FAT back to disk */
if (err_code == dose_noerr)
for (m = 0; m < boot_sec.bpb_fats; m++) {
k = 0;
for (j = first_fat_sector; j <= last_fat_sector; j++) {
writesec(drive_number, j, fat + k);
k = k + bytes_per_sec;
}
first_fat_sector = first_fat_sector + boot_sec.bpb_fat_size;
last_fat_sector = last_fat_sector + boot_sec.bpb_fat_size;
}
/* ---- Write the updated boot sector back to memory disk ---- */
if (err_code == dose_noerr)
if ((result = writesec(drive_number, 0, &boot_sec)) != 0) {
printf("Error -- could not write boot sector back. Code: %1d\n", result);
printf("Reboot recommended.\n");
err_code = dose_invdrv;
}
/*
---- Restore the memory disk's default directory, then reset default
drive.
*/
if (err_code == dose_noerr) {
_rax = dosf_chdir << 8;
_rds = _showds();
_rdx = &mdisk_pathname;
_doint(dosi_dosf);
_rax = dosf_seldisk << 8;
_rdx = default_drive;
_doint(dosi_dosf);
}
/* ---- Free the C-allocated variables. ---- */
free(fat);
/* ---- Display Memory Block Table ----- */
dsp_mem_blk_table();
/*
---- If expanding and EM was not requested, terminate normally by
using terminate and stay resident function.
*/
if (err_code == dose_noerr && !em_requested && mdisk_chg > 0) {
_rax = (dosf_keepprc << 8) + dose_noerr;
_rdx = sec_per_blk * par_per_sec;
_doint(dosi_dosf); /* PROGRAM TERMINATES HERE */
}
exit(err_code);
} /* End of main routine */
/* ==== EXPAND ROUTINE ==== */
/* Expands the memory disk by allocating new memory blocks to it. */
int expand()
{
int result, j;
int mdisk_dif;
/*
---- Size the already existing main memory block to 32K.
We do this twice, once now, and once when we exit and
stay resident. We do it now so as to detect any possible errors.
The real allocation will be done at termination time.
*/
mdisk_dif = mdisk_chg;
if (!em_requested) {
printf("Alocating new %dK memory block #%d\n",
sec_per_blk/sec_per_K, boot_sec.bpb_media);
_rax = dosf_setblk << 8;
_res = pgm_seg;
_rbx = sec_per_blk * par_per_sec;
_doint(dosi_dosf);
if (_carryf == 1) {
printf("Error -- could not modify memory block size. Error code %1d\n",
_rax);
printf("Probably caused by not enough free memory.\n");
return(dose_noram);
}
mdisk_dif = mdisk_dif - sec_per_blk;
/*
Increment the media byte to show an additional memory block has been
allocated. Set the starting paragraph and size (in sectors) for the
new block in the memory block table.
*/
boot_sec.mem_blk_table[boot_sec.bpb_media].typ = nor_flg;
boot_sec.mem_blk_table[boot_sec.bpb_media].par = pgm_seg;
boot_sec.mem_blk_table[boot_sec.bpb_media].siz = sec_per_blk;
boot_sec.mem_blk_table[boot_sec.bpb_media].hdl = 0;
boot_sec.bpb_media = boot_sec.bpb_media + 1;
fatput(boot_sec.bpb_media, 0, fat);
/* Increase the total number of sectors. */
boot_sec.bpb_total = boot_sec.bpb_total + sec_per_blk;
}
/* Now allocate new memory blocks for the remaining desired space. */
for (;mdisk_dif > 0; mdisk_dif = mdisk_dif - sec_per_blk) {
printf("Allocating new %dK memory block #%d\n",
sec_per_blk/sec_per_K,boot_sec.bpb_media);
#if em_support
if (em_requested) {
_rax = emm_get_handle << 8;
_rbx = em_pag_per_blk;
_doint(usri_emm);
_rax = _rax >> 8;
if (_rax == 0) {
boot_sec.mem_blk_table[boot_sec.bpb_media].typ = em_flg;
boot_sec.mem_blk_table[boot_sec.bpb_media].par = em_PFseg;
boot_sec.mem_blk_table[boot_sec.bpb_media].siz = sec_per_blk;
boot_sec.mem_blk_table[boot_sec.bpb_media].hdl = _rdx;
boot_sec.bpb_total = boot_sec.bpb_total + sec_per_blk;
boot_sec.bpb_media = boot_sec.bpb_media + 1;
fatput(boot_sec.bpb_media, 0, fat);
} else {
printf("Error -- could not allocate new memory block from EMM.");
printf(" Error code %2x\n",_rax);
printf("Probably caused by not enough free memory.\n");
break;
}
} else
#endif
{
_rax = dosf_alloc << 8;
_rbx = sec_per_blk * par_per_sec;
_doint(dosi_dosf);
if (_carryf == 1) {
printf("Error -- could not allocate new memory block. Error code %1d\n",
_rax);
printf("Probably caused by not enough free memory.\n");
break;
} else {
boot_sec.mem_blk_table[boot_sec.bpb_media].typ = nor_flg;
boot_sec.mem_blk_table[boot_sec.bpb_media].par = _rax;
boot_sec.mem_blk_table[boot_sec.bpb_media].siz = sec_per_blk;
boot_sec.mem_blk_table[boot_sec.bpb_media].hdl = 0;
boot_sec.bpb_total = boot_sec.bpb_total + sec_per_blk;
boot_sec.bpb_media = boot_sec.bpb_media + 1;
fatput(boot_sec.bpb_media, 0, fat);
}
}
}
return(dose_noerr);
} /* end of expand routine */
/*
==== SHRINK ROUTINE ====
Shrinks the memory disk by moving all file sectors down
to lowest possible locations, then deallocating memory
blocks.
*/
int shrink()
{
/* ==== Data Storage ==== */
/* ---- Root Directory ---- */
struct dir_entry root_dir[dir_per_sec];
/* ---- Other locals ---- */
int cur_dir; /* ptr to current directory */
int dir_sector; /* current directory sector */
int mdisk_dif; /* desired change in sectors */
char tmpstr[20];
int j, result;
mdisk_dif = -mdisk_chg; /* make variable positive */
result = dose_noerr;
/* ---- Calculate the location of the directory sectors. */
first_dir_sector = boot_sec.bpb_reserved +
(boot_sec.bpb_fat_size * boot_sec.bpb_fats);
last_dir_sector = (boot_sec.bpb_root * sizeof(struct dir_entry) /
bytes_per_sec) + first_dir_sector - 1;
/* ---- First data sector follows the last root directory sector. */
first_data_sector = last_dir_sector + 1;
/*
---- Locate the first available cluster in the FAT. First FAT entry
is number 2, not 0, because the first two FAT entries are used
for the media descriptor and filler.
*/
for (free_cluster = 2; fatget(free_cluster, fat) != available;
free_cluster++);
/*
---- Loop through the root directory, packing the clusters of
each file to the lowest possible locations.
*/
for (dir_sector = first_dir_sector; dir_sector <= last_dir_sector;
dir_sector++)
{
readsec(drive_number, dir_sector, &root_dir);
for (cur_dir = 0; cur_dir < dir_per_sec; cur_dir++)
if ((result = pack_file(&(root_dir[cur_dir]), 0, fat)) != dose_noerr)
break;
writesec(drive_number, dir_sector, &root_dir);
if (result != dose_noerr) break;
}
/*
---- Free up memory. Loop backwards through the memory block table,
freeing allocated memory blocks. As we do so, decrement the
media byte (memory block counter) and total sectors.
*/
j = boot_sec.bpb_media - 1;
if (result == dose_noerr) while (mdisk_dif >= sec_per_blk) {
if (mdisk_dif >= boot_sec.mem_blk_table[j].siz) {
printf("Freeing memory block #%d\n",j);
if (boot_sec.mem_blk_table[j].typ == nor_flg) {
_res = boot_sec.mem_blk_table[j].par;
_rax = dosf_dealloc << 8;
_doint(dosi_dosf);
if (_carryf == 1) {
printf ("Error -- could not free allocated memory block #%d.",j);
printf (" Error code %d\n",_rax);
}
}
#if em_support
else {
_rax = emm_fre_handle << 8;
_rdx = boot_sec.mem_blk_table[j].hdl;
_doint(usri_emm);
_rax = _rax >> 8;
if (_rax != 0) {
printf ("Error -- could not free EM pages for memory block #%d.",j);
printf (" Error code %2x\n",_rax);
}
}
#endif
boot_sec.bpb_media = boot_sec.bpb_media - 1;
fatput(boot_sec.bpb_media, 0, fat);
boot_sec.bpb_total = boot_sec.bpb_total -
boot_sec.mem_blk_table[j].siz;
mdisk_dif = mdisk_dif - boot_sec.mem_blk_table[j].siz;
}
j = j - 1;
}
return(result);
} /* end of shrink routine */
/*
==== PACK_FILE ROUTINE ====
Given the directory entry for a file, packs all the clusters
for that file to the lowest possible locations. If the file
is itself a subdirectory, then this routine will be called
recursively via the "move_cluster" routine, i.e., "pack_file
calls "move_cluster" who calls "pack_file" who calls
"move_cluster" who calls "pack_file", etc.
Clusters are packed down by moving any cluster above
"free_cluster" down to "free_cluster". Then the next free
cluster is located and the next cluster is examined.
*/
int pack_file(dir, parent_cluster, fat)
struct dir_entry *dir;
int parent_cluster;
unsigned char fat[];
{
int next_cluster;
int cur_cluster;
int new_cluster;
int result;
/* Is this directory entry erased or never used? If so, do nothing. */
result = dose_noerr;
if (dir->u_name.status != never_used &&
dir->u_name.status != erased) {
/*
If file is not 0 length or it is a subdirectory file,
we can pack it.
*/
if (dir->size > 0 || isdir(dir)) {
/*
Handle the first cluster number, which is stored in the
directory entry, not in the FAT.
*/
next_cluster = dir->first_cluster;
if (next_cluster > free_cluster) {
new_cluster = free_cluster;
fatput(fatget(next_cluster, fat), new_cluster, fat);
dir->first_cluster = new_cluster;
fatput(available, next_cluster, fat);
for (; fatget(free_cluster, fat) != available; free_cluster++);
result = move_cluster(dir, parent_cluster, fat,
drive_number, next_cluster, new_cluster);
}
/* Handle rest of clusters stored in the FAT */
if (result == dose_noerr) for (cur_cluster = dir->first_cluster;
!islast(next_cluster = fatget(cur_cluster, fat));
cur_cluster = fatget(cur_cluster, fat)) {
if (next_cluster > free_cluster) {
new_cluster = free_cluster;
fatput(fatget(next_cluster, fat), new_cluster, fat);
fatput(new_cluster, cur_cluster, fat);
fatput(available, next_cluster, fat);
for (; fatget(free_cluster, fat) != available; free_cluster++);
result = move_cluster(dir, parent_cluster, fat,
drive_number, next_cluster, new_cluster);
if (result != dose_noerr) break;
}
}
}
}
return(result);
}
/*
==== MOVE_CLUSTER ROUTINE ====
Moves all sectors of secified cluster. If the file whose clusters
are being moved is a subdirectory, then call "pack_file" routine
recursively.
*/
int move_cluster (dir, parent_cluster, fat, drive, from, to)
struct dir_entry *dir;
int parent_cluster;
unsigned char fat[];
int drive, from, to;
{
struct dir_entry a_sector[dir_per_sec];
int cur_dir;
int result;
char *tryit;
int j;
result = dose_noerr;
from = (from-2) * boot_sec.sec_per_cluster + first_data_sector;
to = (to-2) * boot_sec.sec_per_cluster + first_data_sector;
for (j = 0; j < boot_sec.sec_per_cluster; j++) {
readsec(drive_number, from + j, &a_sector);
/*
If this sector belongs to a directory file, then we must
recursively call the pack_file routine. The first two entries
in any subdirectory, however, are special. They are named
"." and ".." The first points to the disk location of the
beginning of this subdirectory; the second to the disk location
of the parent directory (0 if root directory)
*/
if (isdir(dir))
for (cur_dir = 0; cur_dir < dir_per_sec; cur_dir++) {
if (a_sector[cur_dir].u_name.name[0] == '.') {
if (a_sector[cur_dir].u_name.name[1] == '.')
a_sector[cur_dir].first_cluster = parent_cluster;
else a_sector[cur_dir].first_cluster = dir->first_cluster;
}
else {
/* In DeSmet C, local variables are allocated on the stack. */
/* Make sure there is sufficient stack space for the recursive */
/* call. If not, display message, return error code, and */
/* calling routines will attempt a graceful exit. Test if */
/* stack has enough room by allocating space from it, then */
/* freeing it. */
if ((tryit = calloc(700, 1)) != 0) {
free(tryit);
pack_file(&(a_sector[cur_dir]), dir->first_cluster, fat);
} else {
printf("Error -- Insufficient stack space to pack ");
printf("subdirectory %s\n", a_sector[cur_dir].u_name.name);
printf("Too many subdirectories. Shrink abandoned. ");
printf("Reboot recommended.\n");
result = dose_noram;
/* Note: Even though we can't handle the subdirectory, */
/* keep going anyway so as to maintain integrity of disk. */
}
}
}
writesec(drive_number, to + j, &a_sector);
}
return(result);
}
/*
==== ISDIR ROUTINE ====
Given a directory entry, returns true if the entry is for a
subdirectory file.
*/
int isdir(dir)
struct dir_entry *dir;
{
if (dir->attr & dir_bit) return(true); else return(false);
}
/*
==== ISLAST ROUTINE ====
Given a FAT entry, returns true if it is an EOF marker.
*/
int islast(cluster)
int cluster;
{
int j;
int result;
result = false;
for (j = last_low; j <= last_high; j++)
if (cluster == j) result = true;
return(result);
}
/*
==== FATGET ROUTINE ====
Given cluster number and FAT, returns the 12-bit FAT entry in
and integer word, right-adjusted.
*/
int fatget (cluster, fat)
int cluster;
unsigned char fat[];
{
int clloc, clword;
clloc = 3*cluster/2;
clword = fat[clloc] + (fat[clloc+1] << 8);
if (cluster & 1) return (clword >> 4); else return (clword & 0x0fff);
}
/*
==== FATPUT ROUTINE ====
Given a 12-bit FAT entry, cluster number, and the FAT, stores
the entry into the FAT.
*/
int fatput (wd12bits, cluster, fat)
int cluster;
int wd12bits;
unsigned char fat[];
{
int clloc, clword;
clloc = 3*cluster/2;
clword = fat[clloc] + (fat[clloc+1] << 8);
if (cluster & 1)
clword = (clword & 0x000f) | (wd12bits << 4);
else
clword = (clword & 0xf000) | wd12bits;
fat[clloc] = clword & 0x00ff;
fat[clloc+1] = clword >> 8;
}
/*
==== DSP_MEM_BLK_TABLE ROUTINE ====
Displays the Memory Block Table on the screen.
*/
dsp_mem_blk_table()
{
int j;
#if em_support
printf("\n");
printf(" Memory Block Table\n");
printf("Block # Paragraph Size(sectors) EM Handle\n");
printf("------- --------- ------------- ---------\n");
for (j = 0; j < boot_sec.bpb_media; ++j) {
printf(" %1d %4x %3d ",
j,boot_sec.mem_blk_table[j].par,boot_sec.mem_blk_table[j].siz);
if (boot_sec.mem_blk_table[j].typ == em_flg)
printf(" %4x",boot_sec.mem_blk_table[j].hdl);
else printf(" --");
printf("\n");
}
#else
printf("\n");
printf(" Memory Block Table\n");
printf("Block # Paragraph Size(sectors)\n");
printf("------- --------- -------------\n");
for (j = 0; j < boot_sec.bpb_media; ++j) {
printf(" %1d %4x %3d\n",
j,boot_sec.mem_blk_table[j].par,boot_sec.mem_blk_table[j].siz);
}
#endif
}
/*
==== PEEKW ROUTINE ====
Given segment and offset, returns the word thereby addressed.
Written because DeSmet C uses small memory model and there are
no FAR data types.
*/
int peekw(off, seg)
unsigned char *off;
int seg;
{
return((_peek(off+1, seg) << 8) + _peek(off, seg));
}
/*
==== READSEC ====
Given drive number, desired logical sector number, and a sector
buffer, this routine reads the sector into the buffer.
NOTE: This routine uses DeSmet's "_doint" routine to perform absolute
disk read interrupt number 25H. This interrupt, unlike most
interrupts, leaves the flags register on the stack when it returns.
In DeSmet this is OK, because the called routine restores the
stack frame prior to RETurning. In some C compilers, this scheme
won't work. For example, in AZTEC C, it is the CALLING routine's
responsibility to restore the stack frame. In AZTEC, then, we
would RETurn to the wrong address and die. So look out, if you are
converting this program to some other version of C.
*/
readsec (drive, sector, buffer)
int drive;
int sector;
char *buffer;
{
_rax = drive;
_rds = _showds(); /* ASSUMPTION: DS = SS in DeSmet C */
_rbx = buffer;
_rcx = 1;
_rdx = sector;
_doint(dosi_dsk_read);
if (_carryf == 1) return (_rax); else return (0);
}
/*
==== WRITESEC ROUTINE ====
Given drive number, logical sector number, and a buffer, this
routine writes the sector to disk.
See the note under routine READSEC.
*/
writesec (drive, sector, buffer)
int drive;
int sector;
char *buffer;
{
_rax = drive;
_rds = _showds(); /* ASSUMPTION: DS = SS in DeSmet C */
_rbx = buffer;
_rcx = 1;
_rdx = sector;
_doint(dosi_dsk_write);
if (_carryf == 1) return (_rax); else return (0);
}
/*
==== DSP_USAGE ROUTINE ====
Displays message how to use program.
*/
int dsp_usage()
{
#if em_support
printf("Usage:\n ADJRAM drive size option\nWhere:\n");
#else
printf("Usage:\n ADJRAM drive size\nWhere:\n");
#endif
printf(" drive = memory disk drive letter, A to I\n");
printf(" size = size of memory disk in K, %d to %d, in following format:\n",
min_size,max_size);
printf(" xxx = set memory disk size to xxx\n");
printf(" +xxx = increase memory disk size by xxx\n");
printf(" -xxx = decrease memory disk size by xxx\n");
printf(" Fxxx = adjust size so there is xxx free space\n");
printf(" Mxxx = ensure there is at least xxx of free space\n");
printf(" Size will be rounded to the next highest %dK\n",
sec_per_blk/sec_per_K);
printf(" If size is omitted, current drive size is displayed.\n");
#if em_support
printf(" option= optional /E if INTEL/LOTUS/Microsoft Expanded Memory\n");
printf(" is to be used for any expansion\n");
#endif
printf("Examples:\n");
printf(" ADJRAM C: 250\n");
printf(" will set memory disk C to 256K\n");
#if em_support
printf(" ADJRAM i: +60 /E\n");
printf(" will increase memory disk I by 64K, using expanded memory\n");
#else
printf(" ADJRAM i: +60\n");
printf(" will increase memory disk I by 64K\n");
#endif
printf(" ADJRAM C: m64\n");
printf(" will ensure there is at least 64K free space on drive C\n");
}